package wsi.exp;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.rygh.core.algorithm.PrecisionRecall;
import org.rygh.core.html.Html;
import org.rygh.core.html.table.HtmlTable;
import org.rygh.core.html.table.TableData;
import org.rygh.core.html.table.TableTuple;
import org.rygh.core.model.common.MapList;
import org.rygh.core.util.UIo;
import org.rygh.core.util.UTime;
import org.rygh.core.xml.UXml;
import org.rygh.core.xml.XmlLeaf;
import org.rygh.core.xml.XmlObject;
import org.rygh.core.xml.XmlParser;
import org.rygh.core.xml.XmlPath;
import org.rygh.core.xml.XmlTree;
import org.rygh.semantic.model.ex.PredicatePath;

import wsi.exp.ExpXml.Tag;
import wsi.function.Function;
import wsi.match.MatchProperty;
import wsi.match.MatchProperty.MatchType;
import wsi.script.AppConfig;

public class Exp2TreeToEntity extends Exp1SelectedTree {

	private final static Map<Function, Boolean> detectResultMap = new HashMap<>();

	public static List<MatchProperty> getFunctionMatchProperty(Function function) {
		Exp2TreeToEntity exp2 = new Exp2TreeToEntity();
		exp2.load2();

		List<MatchProperty> goldMatches = exp2.getGoldSetMap2().get(function);

		return goldMatches;
	}

	public static boolean isFunctionCorrectlyDetected(Function function) {
		if (function == null)
			return false;

		if (detectResultMap.containsKey(function))
			return detectResultMap.get(function);

		Exp2TreeToEntity exp2 = new Exp2TreeToEntity();
		exp2.load2();

		List<MatchProperty> goldMatches = exp2.getGoldSetMap2().get(function);

		return isFunctionCorrectlyDetected(function, goldMatches);
	}

	public static boolean isFunctionCorrectlyDetected(Function function,
			List<MatchProperty> goldMatches) {
		if (function == null)
			return false;

		if (detectResultMap.containsKey(function))
			return detectResultMap.get(function);

		if (!Exp1SelectedTree.isFunctionCorrectlyDetected(function)) {
			detectResultMap.put(function, false);
			return false;
		}

		if (goldMatches == null || goldMatches.isEmpty()) {
			System.out.println("[error] exp2 gold set is empty.");
			return false;
		}

		Exp2TreeToEntity exp2 = new Exp2TreeToEntity();
		exp2.load2();

		MapList<Function, MatchProperty> autoMap = exp2.getAutomaticMap2();
		List<MatchProperty> autoMatches = autoMap.get(function);

		if (autoMatches == null || autoMatches.isEmpty()) {
			System.out.printf("[warn] exp2 function %s is not yet computed.\n",
					function.getFullName());

			return false;
		} else if (!MatchProperty.equalsToAny(autoMatches, goldMatches)) {
			System.out.printf(
					"[warn] exp2 function %s wrong match %s, expect %s.\n",
					function.getFullName(), autoMatches, goldMatches);

			detectResultMap.put(function, false);
			return false;
		}

		detectResultMap.put(function, true);
		return true;
	}

	protected final Map<Function, Boolean> exp2ResultMap = new HashMap<>();
	protected final MapList<Function, MatchProperty> goldSetMap2 = new MapList<>();
	private final MapList<Function, MatchProperty> automaticMap2 = new MapList<>();
	private PrecisionRecall precisionRecall;

	public Exp2TreeToEntity() {

	}

	public MapList<Function, MatchProperty> getAutomaticMap2() {
		return automaticMap2;
	}

	public MapList<Function, MatchProperty> getGoldSetMap2() {
		return goldSetMap2;
	}

	public XmlTree getXmlTree(XmlPath rootPath, MatchProperty matchProperty) {
		XmlTree xml = new XmlTree();
		XmlLeaf parent;
		XmlObject obj;

		obj = new XmlObject(Tag.result.name());
		obj.addAttribute(Tag.experiment.name(), "2");
		obj.addAttribute(Tag.description.name(),
				"Identifying isomorphic subtrees");
		obj.addAttribute(Tag.date.name(), UTime.toTodayString());

		parent = new XmlLeaf(xml.getRoot(), obj);

		obj = new XmlObject(Tag.subtree_root.name(), rootPath.stripIndex());

		new XmlLeaf(parent, obj);
		new XmlLeaf(parent, matchProperty.getXmlObject());

		return xml;
	}

	public int judge2(MapList<Function, MatchProperty> autoMap,
			MapList<Function, MatchProperty> goldMap,
			Map<Function, Boolean> exp1ResMap, Map<Function, Boolean> exp2ResMap) {
		int c = 0;

		for (Function function : autoMap.keySet()) {
			if (!goldMap.containsKey(function)) {
				System.out.printf("[warn] gold set 2 does not contain %s.\n",
						function.getFullName());
				continue;
			}

			if (!exp1ResMap.containsKey(function) || !exp1ResMap.get(function))
				continue;

			List<MatchProperty> pg, pa;
			pg = goldMap.get(function);
			pa = autoMap.get(function);

			if (MatchProperty.equalsToAny(pg, pa)) {
				exp2ResMap.put(function, true);
				c++;
			} else
				exp2ResMap.put(function, false);
		}

		return c;
	}

	public void load2() {
		ExpFolderController folder = ExpFolderController.shared();

		loadXml2(folder.getFolderForExp2(), automaticMap2);

		loadGold2(goldSetMap2);
	}

	@Deprecated
	public void load2(File folder, Map<Function, MatchProperty> map) {
		MatchProperty property;

		for (File file : folder.listFiles()) {
			if (!file.isFile())
				continue;

			String fname = UIo.getFileNameWithoutExtension(file.getName());
			Function function = parseFileName(fname);

			String content = UIo.readFile(file);
			String[] lines = content.split("\r\n");

			property = null;

			for (int i = 0; i < lines.length; i++) {
				String line = lines[i];

				if (line.isEmpty() || line.startsWith("// "))
					continue;

				if (line.equals(MatchType.FORALL.name())) {
					property = new MatchProperty(MatchType.FORALL);
					property.setPredicatePath(new PredicatePath(lines[i + 1]));
				} else if (line.equals(MatchType.EXISTS.name())) {
					property = new MatchProperty(MatchType.EXISTS);
					property.setSubtreeIndex(1);
				}
			}

			assert property != null : function.getFullName();
			map.put(function, property);
		}
	}

	public void loadGold2(File dir, MapList<Function, MatchProperty> gold) {
		loadXml2(dir, gold);
	}

	public void loadGold2(MapList<Function, MatchProperty> gold) {
		loadGold2(ExpFolderController.shared().getGoldFolderForExp2(), gold);
	}

	public void loadXml2(File folder, MapList<Function, MatchProperty> map) {
		MatchProperty property;

		for (File file : folder.listFiles(UXml.XML_FILTER)) {
			if (!file.isFile())
				continue;

			String fname = UIo.getFileNameWithoutExtension(file.getName());
			Function function = parseFileName(fname);

			String content = UIo.readFile(file);
			XmlParser parser = new XmlParser(content);
			XmlTree xmltree = parser.parse();

			assert xmltree != null : content;
			// System.out.println("xmltree " + fname);

			property = null;

			List<XmlLeaf> nodes = xmltree.getNodesByXPath(XmlPath.compose(
					Tag.result, Tag.match));

			for (XmlLeaf node : nodes) {
				property = MatchProperty.getMatchProperty(node.getValue());

				assert property != null : function.getFullName();
				map.put(function, property);
			}
		}
	}

	/**
	 * it should be correct in exp1 to be considered correct in exp2
	 */
	@Override
	public void compute() {
		super.compute();

		load2();

		// load2(folder.getGoldFolderForExp2Debug(), goldSetMap);
		// load2(folder.getFolderForExp2Debug(), automaticMap);

		//

		int goldSize = goldSetMap2.size();
		int resultSize = automaticMap2.size();
		int correctSize = judge2(automaticMap2, goldSetMap2, exp1ResultMap,
				exp2ResultMap);

		precisionRecall = new PrecisionRecall(goldSize);
		precisionRecall.setRelevant(correctSize);
		precisionRecall.setRetrieved(resultSize);
	}

	@Override
	public String save() {
		//

		TableData data = new TableData();
		TableTuple tuple = new TableTuple();

		tuple.add("Item", "Value");
		data.add(tuple);

		tuple = new TableTuple();
		tuple.add("Gold Set Size g", precisionRecall.getGoldSize());
		data.add(tuple);

		tuple = new TableTuple();
		tuple.add("Answer Set Size a", precisionRecall.getRetrieved());
		data.add(tuple);

		tuple = new TableTuple();
		tuple.add("correct(a)", precisionRecall.getRelevant());
		data.add(tuple);

		tuple = new TableTuple();
		tuple.add(
				"Precision correct(a)/a",
				String.format("%.2f%%",
						precisionRecall.getPrecisionInPercentage()));
		data.add(tuple);

		tuple = new TableTuple();
		tuple.add("Recall correct(a)/g", String.format("%.2f%%",
				precisionRecall.getRecallInPercentage()));
		data.add(tuple);

		//

		Html html = new Html("Experiment 2");
		html.addElement(AppConfig.shared().getConfigHtml());

		HtmlTable table = new HtmlTable("Experiment 2, Tree to Entity", data);
		html.addElement(table);
		html.newline();

		data = new TableData();
		tuple = new TableTuple();

		tuple.add("Service", "Function", "Gold Set", "", "Automatic", "");
		data.add(tuple);

		String str1, str2;
		str1 = str2 = "";

		//

		for (Function function : automaticMap2.keySet()) {
			if (!goldSetMap2.containsKey(function))
				continue;

			tuple = new TableTuple();

			tuple.add(function.getService());
			tuple.add(function.getName());

			List<MatchProperty> gs = goldSetMap2.get(function);
			str1 = str2 = "";

			for (MatchProperty g : gs) {
				str1 += g.getType().name() + "<br>";

				if (g.getType() == MatchType.EXISTS)
					str2 += g.getSubtreeIndex() + "<br>";
				else
					str2 += g.getPredicatePath().getKey() + "<br>";
			}

			tuple.add(str1);
			tuple.add(str2);

			List<MatchProperty> as = automaticMap2.get(function);
			str1 = str2 = "";

			for (MatchProperty a : as) {
				str1 += a.getType().name() + "<br>";

				if (a.getType() == MatchType.EXISTS)
					str2 += a.getSubtreeIndex() + "<br>";
				else
					str2 += a.getPredicatePath().getKey() + "<br>";
			}

			tuple.add(str1);
			tuple.add(str2);

			if (!exp1ResultMap.containsKey(function)
					|| !exp1ResultMap.get(function))
				tuple.setBackgroundColor(Color.blue);
			else {
				if (!MatchProperty.equalsToAny(gs, as))
					tuple.setBackgroundColor(Color.red);
			}

			data.add(tuple);
		}

		data.sort();

		appendMissingHtml(html, goldSetMap2.keySet(), automaticMap2.keySet());

		//

		table = new HtmlTable("Experiment 2, Detailed Information", data);
		html.addElement(table);
		html.newline();

		//

		String htmlstr = html.toString();

		ExpFolderController folder = ExpFolderController.shared();
		UIo.saveFile(new File(folder.getResultFolder(), ExpFolderController.EXP
				+ "2_result.html"), htmlstr);

		return htmlstr;
	}
}
